// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2010 Google Inc.  All rights reserved.
// http://code.google.com/p/goprotobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package proto


/*
 * Types and routines for supporting protocol buffer extensions.
 */

import (
	"os"
	"reflect"
	"strconv"
	"unsafe"
)

// ExtensionRange represents a range of message extensions for a protocol buffer.
// Used in code generated by the protocol compiler.
type ExtensionRange struct {
	Start, End int32 // both inclusive
}

// extendableProto is an interface implemented by any protocol buffer that may be extended.
type extendableProto interface {
	ExtensionRangeArray() []ExtensionRange
	ExtensionMap() map[int32]Extension
}

// ExtensionDesc represents an extension specification.
// Used in generated code from the protocol compiler.
type ExtensionDesc struct {
	ExtendedType  interface{} // nil pointer to the type that is being extended
	ExtensionType interface{} // nil pointer to the extension type
	Field         int32       // field number
	Name          string      // fully-qualified name of extension
	Tag           string      // protobuf tag style
}

/*
Extension represents an extension in a message.

When an extension is stored in a message using SetExtension
only desc and value are set. When the message is marshaled
enc will be set to the encoded form of the message.

When a message is unmarshaled and contains extensions, each
extension will have only enc set. When such an extension is
accessed using GetExtension (or GetExtensions) desc and value
will be set.
*/
type Extension struct {
	desc  *ExtensionDesc
	value interface{}
	enc   []byte
}

// SetRawExtension is for testing only.
func SetRawExtension(base extendableProto, id int32, b []byte) {
	base.ExtensionMap()[id] = Extension{enc: b}
}

// isExtensionField returns true iff the given field number is in an extension range.
func isExtensionField(pb extendableProto, field int32) bool {
	for _, er := range pb.ExtensionRangeArray() {
		if er.Start <= field && field <= er.End {
			return true
		}
	}
	return false
}

// checkExtensionTypes checks that the given extension is valid for pb.
func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) os.Error {
	// Check the extended type.
	if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b {
		return os.NewError("bad extended type; " + b.String() + " does not extend " + a.String())
	}
	// Check the range.
	if !isExtensionField(pb, extension.Field) {
		return os.NewError("bad extension number; not in declared ranges")
	}
	return nil
}

// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m.
func encodeExtensionMap(m map[int32]Extension) os.Error {
	for k, e := range m {
		if e.value == nil || e.desc == nil {
			// Extension is only in its encoded form.
			continue
		}

		// We don't skip extensions that have an encoded form set,
		// because the extension value may have been mutated after
		// the last time this function was called.

		et := reflect.TypeOf(e.desc.ExtensionType)
		props := new(Properties)
		props.Init(et, "unknown_name", e.desc.Tag, 0)

		p := NewBuffer(nil)
		// The encoder must be passed a pointer to e.value.
		// Allocate a copy of value so that we can use its address.
		x := reflect.New(et)
		x.Elem().Set(reflect.ValueOf(e.value))
		if err := props.enc(p, props, x.Pointer()); err != nil {
			return err
		}
		e.enc = p.buf
		m[k] = e
	}
	return nil
}

// HasExtension returns whether the given extension is present in pb.
func HasExtension(pb extendableProto, extension *ExtensionDesc) bool {
	// TODO: Check types, field numbers, etc.?
	_, ok := pb.ExtensionMap()[extension.Field]
	return ok
}

// ClearExtension removes the given extension from pb.
func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
	// TODO: Check types, field numbers, etc.?
	pb.ExtensionMap()[extension.Field] = Extension{}, false
}

// GetExtension parses and returns the given extension of pb.
// If the extension is not present it returns (nil, nil).
func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, os.Error) {
	if err := checkExtensionTypes(pb, extension); err != nil {
		return nil, err
	}

	e, ok := pb.ExtensionMap()[extension.Field]
	if !ok {
		return nil, nil // not an error
	}
	if e.value != nil {
		// Already decoded. Check the descriptor, though.
		if e.desc != extension {
			// This shouldn't happen. If it does, it means that
			// GetExtension was called twice with two different
			// descriptors with the same field number.
			return nil, os.NewError("proto: descriptor conflict")
		}
		return e.value, nil
	}

	v, err := decodeExtension(e.enc, extension)
	if err != nil {
		return nil, err
	}

	// Remember the decoded version and drop the encoded version.
	// That way it is safe to mutate what we return.
	e.value = v
	e.desc = extension
	e.enc = nil
	return e.value, nil
}

// decodeExtension decodes an extension encoded in b.
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, os.Error) {
	// Discard wire type and field number varint. It isn't needed.
	_, n := DecodeVarint(b)
	o := NewBuffer(b[n:])

	t := reflect.TypeOf(extension.ExtensionType)
	props := &Properties{}
	props.Init(t, "irrelevant_name", extension.Tag, 0)

	base := unsafe.New(t)
	var sbase uintptr
	if t.Elem().Kind() == reflect.Struct {
		// props.dec will be dec_struct_message, which does not refer to sbase.
		*(*unsafe.Pointer)(base) = unsafe.New(t.Elem())
	} else {
		sbase = uintptr(unsafe.New(t.Elem()))
	}
	if err := props.dec(o, props, uintptr(base), sbase); err != nil {
		return nil, err
	}
	return unsafe.Unreflect(t, base), nil
}

// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
// The returned slice has the same length as es; missing extensions will appear as nil elements.
func GetExtensions(pb interface{}, es []*ExtensionDesc) (extensions []interface{}, err os.Error) {
	epb, ok := pb.(extendableProto)
	if !ok {
		err = os.NewError("not an extendable proto")
		return
	}
	extensions = make([]interface{}, len(es))
	for i, e := range es {
		extensions[i], err = GetExtension(epb, e)
		if err != nil {
			return
		}
	}
	return
}

// TODO: (needed for repeated extensions)
//   - ExtensionSize
//   - AddExtension

// SetExtension sets the specified extension of pb to the specified value.
func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) os.Error {
	if err := checkExtensionTypes(pb, extension); err != nil {
		return err
	}
	typ := reflect.TypeOf(extension.ExtensionType)
	if typ != reflect.TypeOf(value) {
		return os.NewError("bad extension value type")
	}

	pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value}
	return nil
}

// A global registry of extensions.
// The generated code will register the generated descriptors by calling RegisterExtension.

var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)

// RegisterExtension is called from the generated code.
func RegisterExtension(desc *ExtensionDesc) {
	st := reflect.TypeOf(desc.ExtendedType).Elem()
	m := extensionMaps[st]
	if m == nil {
		m = make(map[int32]*ExtensionDesc)
		extensionMaps[st] = m
	}
	if _, ok := m[desc.Field]; ok {
		panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
	}
	m[desc.Field] = desc
}

// RegisteredExtensions returns a map of the registered extensions of a
// protocol buffer struct, indexed by the extension number.
// The argument pb should be a nil pointer to the struct type.
func RegisteredExtensions(pb interface{}) map[int32]*ExtensionDesc {
	return extensionMaps[reflect.TypeOf(pb).Elem()]
}
